package com.thermofisher.dsc.amanda.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.thermofisher.dsc.amanda.common.CommonConstants;
import com.thermofisher.dsc.amanda.common.exception.ExceptionCode;
import com.thermofisher.dsc.amanda.common.util.JwtUtil;
import com.thermofisher.dsc.amanda.model.response.BaseResponse;
import com.thermofisher.dsc.amanda.security.jwt.IJWTInfo;
import com.thermofisher.dsc.amanda.security.jwt.JWTInfo;
import com.thermofisher.dsc.amanda.security.model.SecurityUserDetails;
import com.thermofisher.dsc.amanda.security.service.SecurityUserDetailsService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * @author wenjie.yang on 1/21/2019.
 */
@Slf4j
public class UrlLoginFilter extends OncePerRequestFilter {


  private static final String ENV_DEV = "dev";

  private static final String OPTIONS = "OPTIONS";


  @Value("${bio.ignore}")
  private String ignoreUrl;


  @Value("${bio.env:demo}")
  private String env;

  private ObjectMapper objectMapper = new ObjectMapper();

  @Autowired
  private SecurityUserDetailsService securityUserDetailsService;

  @Autowired
  private JwtUtil jwtUtil;


  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
      FilterChain filterChain) throws ServletException, IOException {
    String jwtToken = request.getHeader(CommonConstants.JWT_HEADER);
    //for dev don't need to login
    if(env.equals(ENV_DEV)){
      try {
        jwtToken = "Bearer "+jwtUtil.generateToken(new JWTInfo("张天雷","1",new ArrayList<>()));
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    String uri = request.getRequestURI();
    try {
      if(!isIgnore(uri) && !request.getMethod().equals(OPTIONS)){
        IJWTInfo ijwtInfo = doJwtParseToken(jwtToken, request);
        refreshToken(ijwtInfo,response);
      }
    }catch (Exception e){
      log.error("username or password error {}",e);
      response.setContentType("application/json");
      response.setCharacterEncoding("utf-8");
      response.setHeader("Access-Control-Allow-Origin","*");
      response.setHeader("Access-Control-Allow-Credentials", "true");
      response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
      response.setHeader("Access-Control-Max-Age", "3600");
      response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
      BaseResponse<Object> errorResponse = new BaseResponse<>(ExceptionCode.USER_TOKEN_FORBIDDEN_OR_EXPIRED,e.getMessage());
      response.getWriter().print(objectMapper.writeValueAsString(errorResponse));
      return ;
    }
    filterChain.doFilter(request, response);
  }

  private Boolean isIgnore(String url){
    String[] split = ignoreUrl.split(",");
    for (String u:split){
      if (url.indexOf(u)== -1 && url.startsWith("/api")){
        return false;
      }
    }
    return true;
  }

  private IJWTInfo doJwtParseToken(String jwtTkoen,HttpServletRequest request) throws Exception {
    if (StringUtils.isBlank(jwtTkoen)){
      throw  new Exception("Authorization is null");
    }
    String authToken = null;
    if (jwtTkoen.indexOf("Bearer")!=-1) {
      authToken = jwtTkoen.replace("Bearer ", "");
    }
    IJWTInfo jwtInfo = null;
    try {
       jwtInfo = jwtUtil.getInfoFromToken(authToken);
    }catch (Exception e){
      throw  new Exception("token is failure or error");
    }

    Collection<GrantedAuthority> authorities = new ArrayList<>();

    List<String> permissions = jwtInfo.getPermissions();
    authorities.add(new SimpleGrantedAuthority("LOGIN_USER"));
    SimpleGrantedAuthority authority;
    for (String permission : permissions) {
      authority = new SimpleGrantedAuthority(permission);
      authorities.add(authority);
    }
    SecurityUserDetails userDetails = new SecurityUserDetails(Long.valueOf(jwtInfo.getId()),
        jwtInfo.getUsername(), null, true, authorities);
    userDetails.setPermissions(jwtInfo.getPermissions());
    if (userDetails != null) {
      UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
          userDetails, null, userDetails.getAuthorities());
      authentication
          .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
      SecurityContextHolder.getContext().setAuthentication(authentication);
    }
    return new JWTInfo(userDetails.getUsername(),userDetails.getId()+"",userDetails.getPermissions());
  }

  private void refreshToken(IJWTInfo jwtInfo, HttpServletResponse response) throws Exception{
    String refreshToken = jwtUtil.generateToken(jwtInfo);
    response.setHeader("Authentication",refreshToken);
  }


}
